#include "Game Engine\Header\Car.h"

#ifndef M_PI
#define M_PI       3.14159265358979323846
#endif

#ifndef M_PI_2
#define M_PI_2     1.57079632679489661923
#endif

#ifndef M_PI_4
#define M_PI_4     0.785398163397448309616
#endif

#define KEYPRESSED( key )	( ( ( GetAsyncKeyState( key ) & 0x8000 ) ) ? 1 : 0 )

#define CUBE_HALF_EXTENTS 1

Car::Car()
{
}

Car::~Car()
{
}

void Car::Init( ID3D10Device* device, btDynamicsWorld* world, std::wstring chassis,
	std::wstring wheels, ID3D10Effect* fx, ID3D10ShaderResourceView* cubeMap, float mass,
	bool useShadows )
{
	btTransform tr;
	tr.setIdentity();

	// Create the chassis based on the size of the mesh
	D3DXVECTOR3 chassisMin;
	D3DXVECTOR3 chassisMax;

	mChassisMesh = new Mesh;
	mChassisMesh->Init( device, chassis, fx, useShadows );
	mChassisMesh->SetCubeMap( cubeMap );

	mChassisMesh->GetAABB( chassisMin, chassisMax );

	mChassisWidth = abs( chassisMax.x - chassisMin.x ) / 2;
	mChassisHeight = abs( chassisMax.y - chassisMin.y ) / 2;
	mChassisLength = abs( chassisMax.z - chassisMin.z ) / 2;

	btCollisionShape* chassisShape = new btBoxShape( btVector3( mChassisWidth,
	mChassisHeight, mChassisLength ) );

	btCompoundShape* compound = new btCompoundShape();
	btTransform localTrans;
	localTrans.setIdentity();

	//localTrans effectively shifts the center of mass with respect to the chassis
	localTrans.setOrigin( btVector3( 0.0f, mChassisHeight * 2.0f, 0.0f ) );

	compound->addChildShape( localTrans, chassisShape );

	// Set the origin for the compound chassis shape and create the rigid body
	tr.setOrigin( btVector3( 0.0f, 0.0f, 0.0f ) );
	mCarChassis = CreateRigidBody( world, mass, tr, compound );//chassisShape);

	Vehicle::Init( device, world, chassis, wheels, fx, cubeMap, mass, useShadows );
}

void Car::SetForkLift( std::wstring lift, std::wstring forkSides,
	std::wstring forkBack, std::wstring connector )
{
	Vehicle::SetForkLift( lift, forkSides, forkBack, connector );
}

void Car::Move( float dt )
{
	// If the shift key is pressed while up, down, left, or right
	// is pressed, move the lift and/or the fork.
	if( GetAsyncKeyState( VK_SHIFT ) & 0x8000 )
	{
		if( mIsForkLift )
		{
			if( GetAsyncKeyState( 'J' ) & 0x8000 )
			{
				mLiftHinge->setLimit( -M_PI / 16.0f, M_PI / 8.0f );
				mLiftHinge->enableAngularMotor( true, -0.1, 10.0 );
			}
			if( GetAsyncKeyState( 'L' ) & 0x8000 )
			{
					
				mLiftHinge->setLimit( -M_PI / 16.0f, M_PI / 8.0f );
				mLiftHinge->enableAngularMotor( true, 0.1, 10.0 );
			}
			if( GetAsyncKeyState( 'I' ) & 0x8000 )
			{
				mForkSlider->setLowerLinLimit( 0.1f );
				mForkSlider->setUpperLinLimit( ( mLiftHeight * 2 ) - 0.1f );
				mForkSlider->setPoweredLinMotor( true );
				mForkSlider->setMaxLinMotorForce( 10.0 );
				mForkSlider->setTargetLinMotorVelocity( 1.0 );
			}
			if( GetAsyncKeyState( 'K' ) & 0x8000 )
			{
				mForkSlider->setLowerLinLimit( 0.1f);
				mForkSlider->setUpperLinLimit( ( mLiftHeight * 2 ) - 0.1f );
				mForkSlider->setPoweredLinMotor( true );
				mForkSlider->setMaxLinMotorForce( 10.0 );
				mForkSlider->setTargetLinMotorVelocity( -1.0 );
			}
		}
	}
	// If the shift key is not pressed, move the vehicle.
	else
	{
		if( GetAsyncKeyState( 'L' ) & 0x8000 )
		{
			mVehicleSteering += mSteeringIncrement;
			if( mVehicleSteering > mSteeringClamp )
				mVehicleSteering = mSteeringClamp;

		}
		if( GetAsyncKeyState( 'J' ) & 0x8000 )
		{
			mVehicleSteering -= mSteeringIncrement;
			if( mVehicleSteering < -mSteeringClamp )
				mVehicleSteering = -mSteeringClamp;

		}
		if( GetAsyncKeyState( 'I' ) & 0x8000 )
		{
			mEngineForce = mMaxEngineForce;
			mBreakingForce = 0.f;
		}
		if( GetAsyncKeyState( 'K' ) & 0x8000 )
		{
			mEngineForce = -mMaxEngineForce;
			mBreakingForce = 0.f;
		}
	}

	// If up or down is not pressed, stop the fork and lift from moving
	// and apply a breaking force to slow down and stop the vehicle.
	if( 0 == ( KEYPRESSED( 'I' ) ) && 0 == ( KEYPRESSED( 'K' ) ) )
	{
		if( mIsForkLift )
			LockForkSlider();
		mEngineForce = 0.f;
		mBreakingForce = mDefaultBreakingForce;
	}
	// If right or left is not pressed, stop the fork and lift from moving.
	if( 0 == ( KEYPRESSED( 'L' ) ) && 0 == ( KEYPRESSED( 'J' ) ) )
	{
		if( mIsForkLift )
			LockLiftHinge();
	}

	// Move the vehicle based on the amount of force applied by pressing
	// up or down or neither and based on the wheel index.
	int wheelIndex = 2;
	mVehicle->applyEngineForce( mEngineForce, wheelIndex );
	mVehicle->setBrake( mBreakingForce, wheelIndex );
	wheelIndex = 3;
	mVehicle->applyEngineForce( mEngineForce, wheelIndex );
	mVehicle->setBrake( mBreakingForce, wheelIndex );

	// Set the steering value for the front tires.
	wheelIndex = 0;
	mVehicle->setSteeringValue( mVehicleSteering, wheelIndex );
	wheelIndex = 1;
	mVehicle->setSteeringValue( mVehicleSteering, wheelIndex );
}

void Car::Draw( D3DXMATRIX& lightViewProj )
{
	Vehicle::Draw( lightViewProj );

	// If the rigid body is a compound shape, get the child shapes and draw each shape
	// separately.
	btTransform cTrans;
	cTrans.setIdentity();
	cTrans.setOrigin( btVector3( 0.0f, mChassisHeight * 2.0f, 0.0f ) );
	btTransform trans = mCarChassis->getWorldTransform() * cTrans;
	mChassisMesh->Draw( BT2DX_MATRIX( trans ), lightViewProj );

	// Get the wheels from the vehicle and their world coordinates and draw each wheel.
	for( int i = 0; i < mVehicle->getNumWheels(); i++ )
	{
		//synchronize the wheels with the (interpolated) chassis worldtransform
		mVehicle->updateWheelTransform( i, true );
		//draw wheels (cylinders)

		btWheelInfo& wheel = mVehicle->getWheelInfo(i);
		btTransform trans = wheel.m_worldTransform;
		mWheelMesh->Draw( BT2DX_MATRIX( trans ), lightViewProj );
	}

	if( mIsForkLift )
	{
		btMotionState* ms2 = mLiftBody->getMotionState();
		mLiftMesh->Draw( BT2DX_MATRIX( *ms2 ), lightViewProj );

		btTransform trans;
		trans.setIdentity();
		trans.setOrigin( btVector3( 0.0f, mChassisHeight * 2.0f,
			mChassisLength + mConnectLength ) );
		btTransform ms3 = mCarChassis->getWorldTransform() * trans;
		mConnectBody->setWorldTransform( ms3 );
		mConnectMesh->Draw( BT2DX_MATRIX( ms3 ), lightViewProj );

		if( mForkBody->getCollisionShape()->isCompound() )
		{
			btCompoundShape* compound = ( btCompoundShape* )mForkBody->getCollisionShape();
			for(int i = 0 ; i < compound->getNumChildShapes(); i++)
			{
				btTransform child = compound->getChildTransform(i);
				btTransform ms = mForkBody->getWorldTransform() * child;
				mForkMesh[i]->Draw( BT2DX_MATRIX( ms ), lightViewProj );
			}
		}
	}
}

void Car::DrawToShadowMap( ID3D10EffectMatrixVariable* mfxBuildShadowMapLightWVPVar,
	ID3D10EffectShaderResourceVariable* mfxBuildShadowMapDiffuseMapVar,
	ID3D10EffectPass* pass,	D3DXMATRIX& lightViewProj )
{
	Vehicle::DrawToShadowMap( mfxBuildShadowMapLightWVPVar, mfxBuildShadowMapDiffuseMapVar,
		pass, lightViewProj );

	for( int i = 0; i < mVehicle->getNumWheels(); i++ )
	{
		//synchronize the wheels with the (interpolated) chassis worldtransform
		mVehicle->updateWheelTransform( i, true );
		//draw wheels (cylinders)

		btWheelInfo& wheel = mVehicle->getWheelInfo(i);
		btTransform trans = wheel.m_worldTransform;
		mWheelMesh->DrawToShadowMap( mfxBuildShadowMapDiffuseMapVar, pass );
	}

	if( mIsForkLift )
	{
		D3DXMATRIX WVP;
		D3DXMATRIX con = BT2DX_MATRIX( mConnectBody->getWorldTransform() );
		WVP = con * lightViewProj;
		mfxBuildShadowMapLightWVPVar->SetMatrix((float*)&WVP);
		mConnectMesh->DrawToShadowMap( mfxBuildShadowMapDiffuseMapVar, pass );

		D3DXMATRIX lift = BT2DX_MATRIX( mLiftBody->getWorldTransform() );
		WVP = lift * lightViewProj;
		mfxBuildShadowMapLightWVPVar->SetMatrix((float*)&WVP);
		mLiftMesh->DrawToShadowMap( mfxBuildShadowMapDiffuseMapVar, pass );

		btCompoundShape* compound;
		compound = ( btCompoundShape* )mForkBody->getCollisionShape();
		for( UINT i = 0; i < compound->getNumChildShapes(); i++ )
		{
			btTransform child = compound->getChildTransform(i);
			btTransform trans;
			trans = mForkBody->getWorldTransform() * child;
			D3DXMATRIX W;
			W = BT2DX_MATRIX( trans );
			WVP = W * lightViewProj;
			mfxBuildShadowMapLightWVPVar->SetMatrix((float*)&WVP);
			mForkMesh[i]->DrawToShadowMap( mfxBuildShadowMapDiffuseMapVar, pass );
		}
	}
}